/**
 *  matrix.inl
 *  AwkwardEngine - Math
 *
 *  Date: 19/08/2013
 *
 *  Language: C++
 *
 *  @author Carl McCaffrey
 *
 */
#ifndef MATRIX_INL
#define MATRIX_INL

/* Initialization */
inline cMat cMatIdentity(void)
{
	cMat mat;
	mat.entries[0] = 1.0f;
	mat.entries[1] = 0.0f;
	mat.entries[2] = 0.0f;
	mat.entries[3] = 0.0f;

	mat.entries[4] = 0.0f;
	mat.entries[5] = 1.0f;
	mat.entries[6] = 0.0f;
	mat.entries[7] = 0.0f;

	mat.entries[8] = 0.0f;
	mat.entries[9] = 0.0f;
	mat.entries[10] = 1.0f;
	mat.entries[11] = 0.0f;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;
}


/* Operations */
inline cMat cMatAdd(cMat mat1, cMat mat2)
{
	cMat mat;
	mat.entries[0] = mat1.entries[0] + mat2.entries[0];
	mat.entries[1] = mat1.entries[1] + mat2.entries[1];
	mat.entries[2] = mat1.entries[2] + mat2.entries[2];
	mat.entries[3] = mat1.entries[3] + mat2.entries[3];

	mat.entries[4] = mat1.entries[4] + mat2.entries[4];
	mat.entries[5] = mat1.entries[5] + mat2.entries[5];
	mat.entries[6] = mat1.entries[6] + mat2.entries[6];
	mat.entries[7] = mat1.entries[7] + mat2.entries[7];

	mat.entries[8] = mat1.entries[8] + mat2.entries[8];
	mat.entries[9] = mat1.entries[9] + mat2.entries[9];
	mat.entries[10] = mat1.entries[10] + mat2.entries[10];
	mat.entries[11] = mat1.entries[11] + mat2.entries[11];

	mat.entries[12] = mat1.entries[12] + mat2.entries[12];
	mat.entries[13] = mat1.entries[13] + mat2.entries[13];
	mat.entries[14] = mat1.entries[14] + mat2.entries[14];
	mat.entries[15] = mat1.entries[15] + mat2.entries[15];

	return mat;
}

inline cMat cMatSubtract(cMat mat1, cMat mat2)
{
	cMat mat;
	mat.entries[0] = mat1.entries[0] - mat2.entries[0];
	mat.entries[1] = mat1.entries[1] - mat2.entries[1];
	mat.entries[2] = mat1.entries[2] - mat2.entries[2];
	mat.entries[3] = mat1.entries[3] - mat2.entries[3];

	mat.entries[4] = mat1.entries[4] - mat2.entries[4];
	mat.entries[5] = mat1.entries[5] - mat2.entries[5];
	mat.entries[6] = mat1.entries[6] - mat2.entries[6];
	mat.entries[7] = mat1.entries[7] - mat2.entries[7];

	mat.entries[8] = mat1.entries[8] - mat2.entries[8];
	mat.entries[9] = mat1.entries[9] - mat2.entries[9];
	mat.entries[10] = mat1.entries[10] - mat2.entries[10];
	mat.entries[11] = mat1.entries[11] - mat2.entries[11];

	mat.entries[12] = mat1.entries[12] - mat2.entries[12];
	mat.entries[13] = mat1.entries[13] - mat2.entries[13];
	mat.entries[14] = mat1.entries[14] - mat2.entries[14];
	mat.entries[15] = mat1.entries[15] - mat2.entries[15];

	return mat;
}

inline cMat cMatMultiply(cMat mat1, cMat mat2)
{
	cMat mat = cMatIdentity();
	mat.entries[0] = ((mat1.entries[0] * mat2.entries[0]) + (mat1.entries[1] * mat2.entries[4]) + (mat1.entries[2] * mat2.entries[8]) + (mat1.entries[3] * mat2.entries[12]));
	mat.entries[1] = ((mat1.entries[0] * mat2.entries[1]) + (mat1.entries[1] * mat2.entries[5]) + (mat1.entries[2] * mat2.entries[9]) + (mat1.entries[3] * mat2.entries[13]));
	mat.entries[2] = ((mat1.entries[0] * mat2.entries[2]) + (mat1.entries[1] * mat2.entries[6]) + (mat1.entries[2] * mat2.entries[10]) + (mat1.entries[3] * mat2.entries[14]));
	mat.entries[3] = ((mat1.entries[0] * mat2.entries[3]) + (mat1.entries[1] * mat2.entries[7]) + (mat1.entries[2] * mat2.entries[11]) + (mat1.entries[3] * mat2.entries[15]));

	mat.entries[4] = ((mat1.entries[4] * mat2.entries[0]) + (mat1.entries[5] * mat2.entries[4]) + (mat1.entries[6] * mat2.entries[8]) + (mat1.entries[7] * mat2.entries[12]));
	mat.entries[5] = ((mat1.entries[4] * mat2.entries[1]) + (mat1.entries[5] * mat2.entries[5]) + (mat1.entries[6] * mat2.entries[9]) + (mat1.entries[7] * mat2.entries[13]));
	mat.entries[6] = ((mat1.entries[4] * mat2.entries[2]) + (mat1.entries[5] * mat2.entries[6]) + (mat1.entries[6] * mat2.entries[10]) + (mat1.entries[7] * mat2.entries[14]));
	mat.entries[7] = ((mat1.entries[4] * mat2.entries[3]) + (mat1.entries[5] * mat2.entries[7]) + (mat1.entries[6] * mat2.entries[11]) + (mat1.entries[7] * mat2.entries[15]));

	mat.entries[8] = ((mat1.entries[8] * mat2.entries[0]) + (mat1.entries[9] * mat2.entries[4]) + (mat1.entries[10] * mat2.entries[8]) + (mat1.entries[11] * mat2.entries[12]));
	mat.entries[9] = ((mat1.entries[8] * mat2.entries[1]) + (mat1.entries[9] * mat2.entries[5]) + (mat1.entries[10] * mat2.entries[9]) + (mat1.entries[11] * mat2.entries[13]));
	mat.entries[10] = ((mat1.entries[8] * mat2.entries[2]) + (mat1.entries[9] * mat2.entries[6]) + (mat1.entries[10] * mat2.entries[10]) + (mat1.entries[11] * mat2.entries[14]));
	mat.entries[11] = ((mat1.entries[8] * mat2.entries[3]) + (mat1.entries[9] * mat2.entries[7]) + (mat1.entries[10] * mat2.entries[11]) + (mat1.entries[11] * mat2.entries[15]));

	mat.entries[12] = ((mat1.entries[12] * mat2.entries[0]) + (mat1.entries[13] * mat2.entries[4]) + (mat1.entries[14] * mat2.entries[8]) + (mat1.entries[15] * mat2.entries[12]));
	mat.entries[13] = ((mat1.entries[12] * mat2.entries[1]) + (mat1.entries[13] * mat2.entries[5]) + (mat1.entries[14] * mat2.entries[9]) + (mat1.entries[15] * mat2.entries[13]));
	mat.entries[14] = ((mat1.entries[12] * mat2.entries[2]) + (mat1.entries[13] * mat2.entries[6]) + (mat1.entries[14] * mat2.entries[10]) + (mat1.entries[15] * mat2.entries[14]));
	mat.entries[15] = ((mat1.entries[12] * mat2.entries[3]) + (mat1.entries[13] * mat2.entries[7]) + (mat1.entries[14] * mat2.entries[11]) + (mat1.entries[15] * mat2.entries[15]));

	return mat;
}

inline cMat cMatTranspose(cMat mat)
{
	cMat mat1;
	mat1.entries[0] = mat.entries[0];
	mat1.entries[1] = mat.entries[4];
	mat1.entries[2] = mat.entries[8];
	mat1.entries[3] = mat.entries[12];

	mat1.entries[4] = mat.entries[1];
	mat1.entries[5] = mat.entries[5];
	mat1.entries[6] = mat.entries[9];
	mat1.entries[7] = mat.entries[13];

	mat1.entries[8] = mat.entries[2];
	mat1.entries[9] = mat.entries[6];
	mat1.entries[10] = mat.entries[10];
	mat1.entries[11] = mat.entries[14];

	mat1.entries[12] = mat.entries[3];
	mat1.entries[13] = mat.entries[7];
	mat1.entries[14] = mat.entries[11];
	mat1.entries[15] = mat.entries[15];

	return mat1;
}

inline cMat cMatTranslate(float x, float y, float z)
{
	cMat mat;
	mat.entries[0] = 1.0f;
	mat.entries[1] = 0.0f;
	mat.entries[2] = 0.0f;
	mat.entries[3] = x;

	mat.entries[4] = 0.0f;
	mat.entries[5] = 1.0f;
	mat.entries[6] = 0.0f;
	mat.entries[7] = y;

	mat.entries[8] = 0.0f;
	mat.entries[9] = 0.0f;
	mat.entries[10] = 1.0f;
	mat.entries[11] = z;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;
}

inline cMat cMatScale(float x, float y, float z)
{
	cMat mat;
	mat.entries[0] = x;
	mat.entries[1] = 0.0f;
	mat.entries[2] = 0.0f;
	mat.entries[3] = 0.0f;

	mat.entries[4] = 0.0f;
	mat.entries[5] = y;
	mat.entries[6] = 0.0f;
	mat.entries[7] = 0.0f;

	mat.entries[8] = 0.0f;
	mat.entries[9] = 0.0f;
	mat.entries[10] = z;
	mat.entries[11] = 0.0f;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;
}

inline cMat cMatRotateX(float rad)
{
	cMat mat;
	mat.entries[0] = 1.0f;
	mat.entries[1] = 0.0f;
	mat.entries[2] = 0.0f;
	mat.entries[3] = 0.0f;

	mat.entries[4] = 0.0f;
	mat.entries[5] = cMathCos(rad);
	mat.entries[6] = -cMathSin(rad);
	mat.entries[7] = 0.0f;

	mat.entries[8] = 0.0f;
	mat.entries[9] = cMathSin(rad);
	mat.entries[10] = cMathCos(rad);
	mat.entries[11] = 0.0f;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;
}

inline cMat cMatRotateY(float rad)
{	
	cMat mat;
	mat.entries[0] = cMathCos(rad);
	mat.entries[1] = 0.0f;
	mat.entries[2] = cMathSin(rad);
	mat.entries[3] = 0.0f;

	mat.entries[4] = 0.0f;
	mat.entries[5] = 1.0f;
	mat.entries[6] = 0.0f;
	mat.entries[7] = 0.0f;

	mat.entries[8] = -cMathSin(rad);
	mat.entries[9] = 0.0f;
	mat.entries[10] = cMathCos(rad);
	mat.entries[11] = 0.0f;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;

}

inline cMat cMatRotateZ(float rad)
{
	cMat mat;
	mat.entries[0] = cMathCos(rad);
	mat.entries[1] = -cMathSin(rad);
	mat.entries[2] = 0.0f;
	mat.entries[3] = 0.0f;

	mat.entries[4] = cMathSin(rad);
	mat.entries[5] = cMathCos(rad);
	mat.entries[6] = 0.0f;
	mat.entries[7] = 0.0f;

	mat.entries[8] = 0.0f;
	mat.entries[9] = 0.0f;
	mat.entries[10] = 1.0f;
	mat.entries[11] = 0.0f;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;
}

inline cMat cMatOrtho(float left, float right, float bottom, float top, float zNear, float zFar)
{
	cMat mat;
	mat.entries[0] = 2.0f / (left - right);
	mat.entries[1] = 0.0f;
	mat.entries[2] = 0.0f;
	mat.entries[3] = -((right + left) / (right - left));

	mat.entries[4] = 0.0f;
	mat.entries[5] = 2.0f / (top - bottom);
	mat.entries[6] = 0.0f;
	mat.entries[7] = -((top + bottom) / (top - bottom));;

	mat.entries[8] = 0.0f;
	mat.entries[9] = 0.0f;
	mat.entries[10] = -2.0f / (zFar - zNear);
	mat.entries[11] = -((zFar + zNear) / (zFar - zNear));;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 0.0f;
	mat.entries[15] = 1.0f;

	return mat;
}

inline cMat cMatPerspective(float fov, float aspect, float zNear, float zFar)
{
	float zRange = zNear - zFar;
	float tanHalfFOV = cMathTan(fov / 2);

	cMat mat;
	mat.entries[0] = 1.0f / (tanHalfFOV * aspect);
	mat.entries[1] = 0.0f;
	mat.entries[2] = 0.0f;
	mat.entries[3] = 0.0f;

	mat.entries[4] = 0.0f;
	mat.entries[5] = 1.0f / tanHalfFOV;
	mat.entries[6] = 0.0f;
	mat.entries[7] = 0.0f;

	mat.entries[8] = 0.0f;
	mat.entries[9] = 0.0f;
	mat.entries[10] = (-zNear - zFar) / zRange;
	mat.entries[11] = 2.0f * zFar * zNear / zRange;

	mat.entries[12] = 0.0f;
	mat.entries[13] = 0.0f;
	mat.entries[14] = 1.0f;
	mat.entries[15] = 0.0f;

	return mat;
}

#endif // MATRIX_INL